package concurrent

import (
	"fmt"
	"strconv"
	"sync"
	"time"
)

var x int64
var wait sync.WaitGroup

//需要传同一个互斥锁，因此指针传递
func add(lock *sync.Mutex) {
	for i := 0; i < 5000; i++ {
		lock.Lock()
		x += 1
		lock.Unlock()
	}

	wait.Done()
}

func TestMutex() {
	wait.Add(2)
	var lock sync.Mutex

	go add(&lock)
	go add(&lock)

	wait.Wait()
	fmt.Println(x)
}

func write(rwlock *sync.RWMutex) {
	rwlock.Lock()
	x = x + 1
	time.Sleep(10 * time.Millisecond)
	rwlock.Unlock()

	wait.Done()
}

func read(rwlock *sync.RWMutex) {
	rwlock.RLock()
	time.Sleep(1000 * time.Millisecond)
	rwlock.RUnlock()

	wait.Done()
}

func TestRWLock() {
	var rwMutex sync.RWMutex
	start := time.Now()

	for i := 0; i < 10; i++ {
		wait.Add(1)
		go write(&rwMutex)
	}

	for i := 0; i < 1000; i++ {
		wait.Add(1)
		go read(&rwMutex)
	}

	wait.Wait()
	end := time.Now()
	fmt.Println(x)
	fmt.Println(end.Sub(start))
}

type S struct {
	name string
}

var s S = S{name: "aa"}

func GetInstance() S {
	return s
}

var once sync.Once
var s1 S

func GetInstance2() S {
	once.Do(func() {
		s1 = S{name: "bb"}
	})

	return s1
}

func TestSyncMap() {
	var m = sync.Map{}
	wait := sync.WaitGroup{}

	for i := 0; i < 20; i++ {
		wait.Add(1)

		go func(n int) {
			key := strconv.Itoa(n)
			m.Store(key, n)
			value, _ := m.Load(key)
			fmt.Println(key, value)

			wait.Done()
		}(i)
	}

	wait.Wait()
}

var (
	lockerZero = new(sync.Mutex)
	condZero   = sync.NewCond(lockerZero)

	locker100 = new(sync.Mutex)
	cond100   = sync.NewCond(locker100)

	lockerData = new(sync.Mutex)

	i = 0
)

func Add() {
	cond100.L.Lock()
	for i >= 100 {
		cond100.Wait()
	}

	i++
	fmt.Println(i)
	cond100.L.Unlock()

	if i > 0 {
		condZero.Broadcast()
	}
}

func Del() {
	condZero.L.Lock()
	for i <= 0 {
		condZero.Wait()
	}

	i--
	fmt.Println(i)
	condZero.L.Unlock()

	if i < 100 {
		cond100.Broadcast()
	}
}

func TestCondition() {
	go Add()
	go Del()

	time.Sleep(1000 * time.Second)
}
